package com.think.admin.web.controller;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.IoUtil;
import com.think.admin.annotations.MenuAnnotation;
import com.think.admin.annotations.ResourceAnnotation;
import com.think.admin.cons.AdminCons;
import com.think.admin.model.dto.UserInfoDTO;
import com.think.admin.model.dto.role.RoleResourceDTO;
import com.think.admin.model.dto.user.UserRoleDto;
import com.think.admin.model.param.home.InfoParam;
import com.think.admin.model.param.home.PasswordParam;
import com.think.admin.oauth.util.OAuthUtil;
import com.think.admin.util.IEnumUtil;
import com.think.admin.web.service.IHomeService;
import com.think.admin.web.service.IResourceService;
import com.think.admin.web.service.ISysUserService;
import com.think.common.enums.StateEnum;
import com.think.common.exception.SysException;
import com.think.common.utils.ResultUtil;
import com.think.common.utils.SysUtils;
import com.think.common.web.controller.SuperController;
import com.think.dao.entity.Resource;
import com.think.dao.entity.SysUser;
import com.think.dao.enums.ResourcePermissionTypeEnum;
import com.think.dao.enums.ResourceTypeEnum;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RedissonClient;
import org.springframework.http.HttpEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * home
 * </p>
 *
 * @author xwj
 * @date 2019/4/26
 */
@RestController
@RequestMapping(value = "admin/home")
@RequiredArgsConstructor
@MenuAnnotation(name = "主页", sort = 0, permissionType = ResourcePermissionTypeEnum.LOGIN)
public class HomeController extends SuperController {
    private final HttpServletResponse response;
    private final RedissonClient redissonClient;
    private final ISysUserService sysUserService;
    private final IHomeService homeService;
    private final ConsumerTokenServices tokenServices;
    private final IResourceService resourceService;

    @GetMapping("/{enumName}")
    @ResourceAnnotation(name = "枚举信息", sort = 1, permissionType = ResourcePermissionTypeEnum.LOGIN)
    public HttpEntity get(@PathVariable("enumName") String enumName) {
        // 根据枚举名称获取枚举类
        if(SysUtils.isEmpty(enumName)){
            return ResultUtil.requestParamError();
        }

        // 返回
        return ResultUtil.success(IEnumUtil.ENUM_MAP.get(enumName));
    }

    @GetMapping("/verifyCode/{token}")
    public HttpEntity verifyCode(@PathVariable String token) throws IOException {
        // 参数校验
        if (SysUtils.isEmpty(token)) {
            return ResultUtil.requestParamError();
        }

        // 声明输出流
        @Cleanup
        ServletOutputStream outputStream = response.getOutputStream();
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        // 定义图形验证码的长和宽,生成验证码（第三个参数是指生成验证码的个数）
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(120, 40, 4, 4);

        // 生成验证码图片（4位验证）
        Random random = new Random();
        StringBuilder code = new StringBuilder();

        for (int i = 0; i < 4; i++) {
            code.append(String.valueOf(random.nextInt(10)));
        }
        Image verifyCodeImage = captcha.createImage(code.toString());

        // 设置验证码 180 秒过期
        redissonClient.getBucket(AdminCons.Redis.VERIFY_CODE + token)
                .set(code, AdminCons.Param.IMAGE_CODE_TIME_OUT, TimeUnit.SECONDS);

        // 获取验证码图片，并写入流
        ImgUtil.writePng(verifyCodeImage, out);
        IoUtil.write(outputStream, false, out.toByteArray());

        // 返回
        return ResultUtil.success();
    }

    @DeleteMapping("/token")
    @ResourceAnnotation(name = "登出", sort = 2, permissionType = ResourcePermissionTypeEnum.LOGIN)
    public HttpEntity logout(HttpServletRequest request) {
        tokenServices.revokeToken(request.getHeader("Authorization")
                .substring(AdminCons.Param.TOKEN_PREFIX.length()));
        return ResultUtil.success();
    }

    @GetMapping("/info")
    @ResourceAnnotation(name = "获取当前登录用户信息", sort = 3, permissionType = ResourcePermissionTypeEnum.LOGIN)
    public HttpEntity info() {
        // userId
        String userId = OAuthUtil.getUserId();

        // 对象查询
        UserInfoDTO userInfoDTO = sysUserService.getById(userId).convert(UserInfoDTO.class);
        if (SysUtils.isEmpty(userInfoDTO)) {
            return ResultUtil.getError();
        }

        // 权限表达式容器
        List<String> permissionList = new ArrayList<>();

        // 超级管理员从数据库取出所有权限信息
        if (AdminCons.Param.SUPER_ADMIN_ID.equals(userId)) {
            resourceService.lambdaQuery()
                    .select(Resource::getPermission)
                    .eq(Resource::getState, StateEnum.ENABLE)
                    .eq(Resource::getType, ResourceTypeEnum.RESOURCE).list()
                    .forEach(r -> {
                        if (SysUtils.isEmpty(r) || SysUtils.isEmpty(r.getPermission())) {
                            return;
                        }

                        // 解析权限表达式
                        String[] permissionArray = r.getPermission().split(":");
                        if (SysUtils.isEmpty(permissionArray)) {
                            return;
                        }

                        // 提取参数
                        String methodType = permissionArray[0];
                        String permission = permissionArray[1];
                        if (SysUtils.isNull(methodType, permission)) {
                            return;
                        }

                        // 组装
                        permissionList.add(methodType + ":" + permission);
                    });

            // 返回
            return ResultUtil.success(userInfoDTO.setPermissionList(permissionList));
        }

        // 其他角色，从缓存中获取角色信息
        Map<String, UserRoleDto> userRoleMap = redissonClient.getMap(AdminCons.Redis.USER_ROLE + userId);
        if (SysUtils.isNotEmpty(userRoleMap)) {
            userRoleMap.keySet().forEach(key -> {
                Map<String, RoleResourceDTO> roleResource = redissonClient.getMap(AdminCons.Redis.ROLE_RESOURCE + key);
                if (SysUtils.isEmpty(roleResource)) {
                    return;
                }
                roleResource.values().forEach(rr -> {
                    if (SysUtils.isEmpty(rr) || SysUtils.isEmpty(rr.getPermission())) {
                        return;
                    }

                    // 解析权限表达式
                    String[] permissionArray = rr.getPermission().split(":");
                    if (SysUtils.isEmpty(permissionArray)) {
                        return;
                    }

                    // 提取参数
                    String methodType = permissionArray[0];
                    String permission = permissionArray[1];
                    if (SysUtils.isNull(methodType, permission)) {
                        return;
                    }

                    // 组装
                    permissionList.add(methodType + ":" + permission);
                });
            });
        }

        // 加载只需登录资源的权限表达式
        resourceService.lambdaQuery()
                .select(Resource::getPermission)
                .eq(Resource::getState, StateEnum.ENABLE)
                .eq(Resource::getPermissionType, ResourcePermissionTypeEnum.LOGIN)
                .eq(Resource::getType, ResourceTypeEnum.RESOURCE).list().forEach(r -> {
            if (SysUtils.isEmpty(r) || SysUtils.isEmpty(r.getPermission())) {
                return;
            }

            // 解析权限表达式
            String[] permissionArray = r.getPermission().split(":");
            if (SysUtils.isEmpty(permissionArray)) {
                return;
            }

            // 提取参数
            String methodType = permissionArray[0];
            String permission = permissionArray[1];
            if (SysUtils.isNull(methodType, permission)) {
                return;
            }

            // 组装
            permissionList.add(methodType + ":" + permission);
        });

        // 返回
        return ResultUtil.success(userInfoDTO.setPermissionList(permissionList));
    }

    @PutMapping("/info")
    @ResourceAnnotation(name = "修改当前登录用户信息", sort = 4, permissionType = ResourcePermissionTypeEnum.LOGIN)
    public HttpEntity updateInfo(@RequestBody @Validated InfoParam infoParam) {
        // 修改
        SysUser sysUser = new SysUser()
                .setUserId(OAuthUtil.getUserId())
                .setName(infoParam.getName());
        boolean updateIsSuccess = sysUserService.updateById(sysUser);
        if (updateIsSuccess) {
            return ResultUtil.success();
        } else {
            return ResultUtil.updateError();
        }
    }

    @PutMapping("/password")
    @ResourceAnnotation(name = "修改密码", sort = 5, permissionType = ResourcePermissionTypeEnum.LOGIN)
    public HttpEntity password(@RequestBody @Validated PasswordParam passwordParam) {
        // 查询账号信息
        SysUser sysUser = sysUserService.getById(OAuthUtil.getUserId());
        if (SysUtils.isEmpty(sysUser)) {
            return ResultUtil.getError();
        }

        // 提取参数
        String oldPwd = passwordParam.getOldPassword();
        String newPassword = passwordParam.getNewPassword();

        // 加密方式
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();

        // 对比密码
        if (!bCryptPasswordEncoder.matches(oldPwd, sysUser.getPwd())) {
            throw new SysException("原密码不正确");
        }

        // 修改
        sysUser.setPwd(bCryptPasswordEncoder.encode(newPassword));
        boolean updateIsSuccess = sysUserService.updateById(sysUser);
        if (updateIsSuccess) {
            return ResultUtil.success();
        } else {
            return ResultUtil.updateError();
        }
    }

    @GetMapping("/menu")
    @ResourceAnnotation(name = "获取菜单", sort = 6, permissionType = ResourcePermissionTypeEnum.LOGIN)
    public HttpEntity menu() {
        return homeService.menu(OAuthUtil.getUserId());
    }

}
